package com.github.netty.core;

import com.github.netty.constant.Constant;
import com.github.netty.holder.NettyHolder;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.*;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.nio.charset.StandardCharsets;

/**
 * @author ArchieSean
 * @description 抽象nettyServer服务处理，保留扩充点
 * @date 2024-08-11
 */
public abstract class AbstractNettyServerHandler extends SimpleChannelInboundHandler<Object> {


    /**
     * 处理客户端向服务端发起 http 握手请求的业务
     * WebSocket在建立握手时，数据是通过HTTP传输的。但是建立之后，在真正传输时候是不需要HTTP协议的。
     * WebSocket 连接过程：
     * 1. 客户端发起http请求，经过3次握手后，建立起TCP连接；http请求里存放WebSocket支持的版本号等信息，如：Upgrade、Connection、WebSocket-Version等；
     * 2. 服务器收到客户端的握手请求后，同样采用HTTP协议回馈数据；
     * 3. 客户端收到连接成功的消息后，开始借助于TCP传输信道进行全双工通信
     *
     * @param ctx     ctx
     * @param request req
     */
    public void handHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
        //如果失败或者请求不是客户端向服务端发起的http请求，则响应错误消息
        if (!request.decoderResult().isSuccess()
                || !Constant.WEBSOCKET.equals(request.headers().get(Constant.HEADER_UPGRADE))) {
            sendHttpResponse(ctx, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        //用于处理握手
        newHandshake(request);
        WebSocketServerHandshaker handshake = getHandshake();
        if (ObjectUtils.isEmpty(handshake)) {
            //如果为空，返回响应：不受支持的websocket版本
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        } else {
            handlerHandshake(ctx, request);
        }

    }

    /*
    获取握手对象
     */
    public abstract WebSocketServerHandshaker getHandshake();

    /**
     * 用于处理握手
     *
     * @param ctx       ctx
     * @param request   request
     */
    public abstract void handlerHandshake(ChannelHandlerContext ctx, FullHttpRequest request);

    /***
     * 用于创建握手
     * @param request request
     */
    public abstract void newHandshake(FullHttpRequest request);

    /**
     * 用于清除对当前会话
     *
     * @param channel 当前信道
     */
    public void clearSession(Channel channel) {
        //获取自定义缓存key
        String cacheKey = NettyHolder.getCacheKey(channel);
        //清除当前会话
        NettyHolder.CHANNELS.remove(channel);

        if (StringUtils.hasLength(cacheKey)) {
            NettyHolder.CHANNEL_MAP.remove(cacheKey);
        }
    }


    /**
     * 用于处理业务
     *
     * @param ctx   ctx
     * @param frame frame
     */
    public void handWebsocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
        // 判断是否是关闭链路的指令
        if (frame instanceof CloseWebSocketFrame) {
            //关闭握手
            stopHandShaker(ctx, frame);
            clearSession(ctx.channel());
            return;
        }
        // 判断是否是 Ping 消息
        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(frame.content()));
            return;
        }
        //判断是否是pong消息
        if (frame instanceof PongWebSocketFrame) {
            ctx.writeAndFlush(new PongWebSocketFrame(frame.content()));
            return;
        }
        //是否二进制消息
        if (!(frame instanceof TextWebSocketFrame)) {
            // 抛出异常，不支持二进制消息
            throw new UnsupportedOperationException(
                    String.format("%s frame types not supported", frame.getClass().getName()));
        } else {
            //服务端接收到的消息，是否广播和点对点，自行实现对应的service接口
            handlerService(ctx.channel(), frame);
        }
    }

    /**
     * @param channel 当前的信道
     * @param frame   套接字
     *                用于消息处理，业务能力
     */
    protected abstract void handlerService(Channel channel, WebSocketFrame frame);

    /*
    用于停止握手操作
     */
    public abstract void stopHandShaker(ChannelHandlerContext ctx, WebSocketFrame frame);

    /**
     * 非netty链接时的响应
     *
     * @param ctx      ctx
     * @param response resp
     */
    public void sendHttpResponse(ChannelHandlerContext ctx, DefaultFullHttpResponse response) {
        //非成功请求
        if (response.status().code() != Constant.SUCCESS_CODE) {
            //创建缓冲区
            ByteBuf byteBuf = Unpooled.copiedBuffer(response.status().toString(), StandardCharsets.UTF_8);
            response.content().writeBytes(byteBuf);
            byteBuf.release();
        }

        //写入请求，服务端向客户端发送数据
        ChannelFuture channelFuture = ctx.channel().writeAndFlush(response);
        if (response.status().code() != Constant.SUCCESS_CODE) {
            //关闭监听
            channelFuture.addListener(ChannelFutureListener.CLOSE);
        }
    }

}
